#include <iostream>
#include <string>
#include <vector>
#include <chrono>
#include <thread>

#include <sw/redis++/redis++.h>

#include "util.hpp"

using std::cout;
using std::endl;
using std::string;
using std::vector;

namespace redis = sw::redis;

void test_get_and_set(redis::Redis &redis){
    // 先清空数据库
    redis.flushall();
    redis.set("key1", "111");
    redis.set("key2", "222");
    redis.set("key3", "333");

    // 因为 get 返回的是 Optional 类型，首先需要取其中的value
    // 其次如果没有数据，使用会抛异常，需要先判断，optional 可以隐式转化为 bool
    auto value1 = redis.get("key1");
    if(value1){
        cout << "value1: " << value1.value() << endl;
    }
    auto value2 = redis.get("key2");
    if(value2){
        cout << "value2: " << value2.value() << endl;
    }
    auto value3 = redis.get("key3");
    if(value3){
        cout << "value3: " << value3.value() << endl;
    }
    auto value4 = redis.get("key4");
    if(value4){
        cout << "value4: " << value4.value() << endl;
    }
    else{
        cout << "key4 不存在" << endl;
    }

    // 测试用 get 获取非 string
    redis.hset("hash1", "key1", "111");
    try{
        auto value = redis.get("hash1");
        if(value){
            cout << "hash1: " << value.value() << endl;
        }
        else{
            cout << "value 不存在" << endl;
        }
    }
    catch(const redis::ReplyError& e){
        cout << "get 失败：" << e.what() << endl;
    }
}

// set 的选项
void test_set_option(redis::Redis &redis){
    // 先清空数据库
    redis.flushall();

    // 设置超时时间
    using namespace std::chrono_literals;
    // redis.set("key1", "111", 10s);
    redis.set("key1", "111", std::chrono::seconds(10));
    // 等待 4s 获取剩余超时时间
    std::this_thread::sleep_for(4s);
    auto ttl = redis.ttl("key1");
    cout << "key1 的剩余超时时间: " << ttl << endl;


    // 修改 —— 当值存在时才生效
    // 没有重载单独的 NX 和 XX，必须携带超时时间
    redis.set("key1", "222", 0s, redis::UpdateType::EXIST);
    auto value = redis.get("key1");
    cout << "key1: " << value.value() << endl;

    // 新增 —— 当值不存在时才生效
    redis.set("key1", "333", 0s, redis::UpdateType::NOT_EXIST);
    value = redis.get("key1");
    cout << "key1: " << value.value() << endl;
}

void test_append(redis::Redis &redis){
    // 先清空数据库
    redis.flushall();

    // 先设置一个字符串
    redis.set("key1", "hello");
    auto value = redis.get("key1");
    cout << "key1: " << value.value() << endl;
    // 追加
    auto res = redis.append("key1", " world");
    cout << "key1 的长度: " << res << endl;
    value = redis.get("key1");
    cout << "key1: " << value.value() << endl;
}

// 测试 setrange 和 getrange
void test_set_and_get_range(redis::Redis &redis){
    // 先清空数据库
    redis.flushall();

    // 先设置一个字符串
    redis.set("key1", "hello world");
    auto value = redis.get("key1");
    cout << "key1: " << value.value() << endl;
    // getrange —— 返回值是 string
    auto res = redis.getrange("key1", 2, 5);
    cout << "key1 的第 2 到 5 个字符: " << res << endl;

    // setrange —— 返回值是修改后的 string 长度
    auto len = redis.setrange("key1", 2, "redis");
    cout << "key1 的长度: " << len << endl;

    value = redis.get("key1");
    cout << "key1: " << value.value() << endl;
}

// 测试 mget 和 mset
void test_mget_and_mset(redis::Redis &redis){
    // 先清空数据库
    redis.flushall();
    // 设置值
    redis.mset({std::make_pair("key1", "zhangsan"), std::make_pair("key2", "lisi"), std::make_pair("key3", "wangwu")});
    // 获取值
    vector<redis::OptionalString> res;
    auto ptr = std::back_insert_iterator(res);
    redis.mget({"key1", "key2", "key3"}, ptr);

    for(auto& e : res){
        cout << e.value() << endl;
    }
}

// 测试 incr 和 decr
void test_incr_and_decr(redis::Redis &redis){
    // 先清空数据库
    redis.flushall();
    // 设置值
    redis.set("key1", "10");
    auto res = redis.incr("key1");
    cout << "key1: " << res << endl;
    redis.incrby("key1", 1000);

    res = redis.decr("key1");
    cout << "key1: " << res << endl;
}

int main(){

    const string connect = "tcp://666666@127.0.0.1:6379";
    redis::Redis redis(connect);

    // test_get_and_set(redis);
    // test_set_option(redis);
    // test_append(redis);
    // test_set_and_get_range(redis);
    // test_mget_and_mset(redis);
    test_incr_and_decr(redis);

    return 0;
}
